
package com.codeminders.imageshackdroid.auth;

import android.accounts.Account;
import android.accounts.AccountAuthenticatorActivity;
import android.accounts.AccountManager;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.ContentResolver;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.provider.ContactsContract;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.widget.EditText;
import android.widget.TextView;
import com.codeminders.imageshackdroid.Constants;
import com.codeminders.imageshackdroid.R;

import java.io.IOException;

/**
 * Activity which displays login screen to the user.
 */
public class AuthenticatorActivity extends AccountAuthenticatorActivity
{

    /** The Intent flag to confirm credentials. **/
    public static final String PARAM_CONFIRM_CREDENTIALS = "confirmCredentials";

    /** The Intent extra to store password. **/
    public static final String PARAM_PASSWORD            = "password";

    /** The Intent extra to store username. **/
    public static final String PARAM_USERNAME            = "username";

    private AccountManager     mAccountManager;
    private Thread             mAuthThread;
    private String             mAuthtoken;

    /**
     * If set we are just checking that the user knows their credentials; this
     * doesn't cause the user's password to be changed on the device.
     */
    private Boolean            mConfirmCredentials       = false;

    /** for posting authentication attempts back to UI thread */
    private final Handler      mHandler                  = new Handler();

    private TextView           mMessage;
    private String             mPassword;
    private EditText           mPasswordEdit;

    /** Was the original caller asking for an entirely new account? */
    protected boolean          mRequestNewAccount        = false;
    private String             mUsername;
    private EditText           mUsernameEdit;

    /**
     * {@inheritDoc}
     */
    @Override
    public void onCreate(Bundle icicle)
    {

        Log.i(Constants.TAG, "onCreate(" + icicle + ")");
        super.onCreate(icicle);
        mAccountManager = AccountManager.get(this);
        Log.i(Constants.TAG, "loading data from Intent");
        final Intent intent = getIntent();
        mUsername = intent.getStringExtra(PARAM_USERNAME);
        mRequestNewAccount = mUsername == null;
        mConfirmCredentials = intent.getBooleanExtra(PARAM_CONFIRM_CREDENTIALS, false);
        Log.i(Constants.TAG, "    request new: " + mRequestNewAccount);
        requestWindowFeature(Window.FEATURE_LEFT_ICON);
        setContentView(R.layout.login_activity);
        getWindow().setFeatureDrawableResource(Window.FEATURE_LEFT_ICON, android.R.drawable.ic_dialog_alert);
        mMessage = (TextView) findViewById(R.id.message);
        mUsernameEdit = (EditText) findViewById(R.id.username_edit);
        mPasswordEdit = (EditText) findViewById(R.id.password_edit);
        mUsernameEdit.setText(mUsername);
        mMessage.setText(getMessage());
    }

    /*
     * {@inheritDoc}
     */
    @Override
    protected Dialog onCreateDialog(int id)
    {
        final ProgressDialog dialog = new ProgressDialog(this);
        dialog.setMessage(getText(R.string.ui_activity_authenticating));
        dialog.setIndeterminate(true);
        dialog.setCancelable(true);
        dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
            public void onCancel(DialogInterface dialog)
            {
                Log.i(Constants.TAG, "dialog cancel has been invoked");
                if(mAuthThread != null)
                {
                    mAuthThread.interrupt();
                    finish();
                }
            }
        });
        return dialog;
    }

    /**
     * Handles onClick event on the Submit button. Sends username/password to
     * the server for authentication.
     * 
     * @param view The Submit button for which this method is invoked
     */
    public void handleLogin(View view)
    {
        if(mRequestNewAccount)
        {
            mUsername = mUsernameEdit.getText().toString();
        }
        mPassword = mPasswordEdit.getText().toString();
        if(TextUtils.isEmpty(mUsername) || TextUtils.isEmpty(mPassword))
        {
            mMessage.setText(getMessage());
        } else
        {
            showProgress();
            // Start authenticating...
            mAuthThread = attemptAuth(mUsername, mPassword, mHandler, AuthenticatorActivity.this);
        }
    }

    private Thread attemptAuth(final String username, final String password, final Handler handler,
            final AuthenticatorActivity context)
    {
        final Runnable runnable = new Runnable() {
            public void run()
            {
                String res;
                try
                {
                    res = ImageshackAccountAuthenticator.onlineConfirmPassword(username, password);
                    Log.d(Constants.TAG, "Auth " + (res != null ? "OK" : "FAILED"));
                    if (res != null) {
                        mUsername = res;
                    }
                } catch(IOException e)
                {
                    Log.e(Constants.TAG, "Auth error", e);
                    res = null;
                }
                final boolean result = res != null;
                handler.post(new Runnable() {
                    public void run()
                    {
                        ((AuthenticatorActivity) context).onAuthenticationResult(result);
                    }
                });
            }
        };

        final Thread t = new Thread() {
            @Override
            public void run()
            {
                try
                {
                    runnable.run();
                } finally
                {
                }
            }
        };
        t.start();
        return t;
    }

    /**
     * Called when response is received from the server for confirm credentials
     * request. See onAuthenticationResult(). Sets the
     * AccountAuthenticatorResult which is sent back to the caller.
     * 
     * @param the confirmCredentials result.
     */
    private void finishConfirmCredentials(boolean result)
    {
        Log.i(Constants.TAG, "finishConfirmCredentials()");
        final Account account = new Account(mUsername, getString(R.string.ACCOUNT_TYPE));
        mAccountManager.setPassword(account, mPassword);
        final Intent intent = new Intent();
        intent.putExtra(AccountManager.KEY_BOOLEAN_RESULT, result);
        setAccountAuthenticatorResult(intent.getExtras());
        setResult(RESULT_OK, intent);
        finish();
    }

    /**
     * Called when response is received from the server for authentication
     * request. See onAuthenticationResult(). Sets the
     * AccountAuthenticatorResult which is sent back to the caller. Also sets
     * the authToken in AccountManager for this account.
     * 
     * @param the confirmCredentials result.
     */
    private void finishLogin()
    {

        Log.i(Constants.TAG, "finishLogin()");
        final Account account = new Account(mUsername, getString(R.string.ACCOUNT_TYPE));
        if(mRequestNewAccount)
        {
            mAccountManager.addAccountExplicitly(account, mPassword, null);
            // Set contacts sync for this account.
            ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true);
        } else
        {
            mAccountManager.setPassword(account, mPassword);
        }
        final Intent intent = new Intent();
        mAuthtoken = mPassword;
        intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, mUsername);
        intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE, getString(R.string.ACCOUNT_TYPE));
        intent.putExtra(AccountManager.KEY_AUTHTOKEN, mAuthtoken);
        setAccountAuthenticatorResult(intent.getExtras());
        setResult(RESULT_OK, intent);
        finish();
    }

    /**
     * Hides the progress UI for a lengthy operation.
     */
    private void hideProgress()
    {
        try {
            dismissDialog(0);
        } catch(IllegalArgumentException e) {
            Log.e(Constants.TAG, e.getMessage());
	    }
    }

    /**
     * Called when the authentication process completes (see attemptLogin()).
     */
    public void onAuthenticationResult(boolean result)
    {

        Log.i(Constants.TAG, "onAuthenticationResult(" + result + ")");
        // Hide the progress dialog
        hideProgress();
        if(result)
        {
            if(!mConfirmCredentials)
            {
                finishLogin();
            } else
            {
                finishConfirmCredentials(true);
            }
        } else
        {
            Log.e(Constants.TAG, "onAuthenticationResult: failed to authenticate");
            if(mRequestNewAccount)
            {
                // "Please enter a valid username/password.
                mMessage.setText(getText(R.string.login_activity_loginfail_text_both));
            } else
            {
                // "Please enter a valid password." (Used when the
                // account is already in the database but the password
                // doesn't work.)
                mMessage.setText(getText(R.string.login_activity_loginfail_text_pwonly));
            }
        }
    }

    /**
     * Returns the message to be displayed at the top of the login dialog box.
     */
    private CharSequence getMessage()
    {
        if(TextUtils.isEmpty(mUsername))
        {
            // If no username, then we ask the user to log in using an
            // appropriate service.
            final CharSequence msg = getText(R.string.login_activity_newaccount_text);
            return msg;
        }
        if(TextUtils.isEmpty(mPassword))
        {
            // We have an account but no password
            return getText(R.string.login_activity_loginfail_text_pwmissing);
        }
        return null;
    }

    /**
     * Shows the progress UI for a lengthy operation.
     */
    private void showProgress()
    {
        showDialog(0);
    }
}
